package com.gitee.wsl.coroutines.atomic

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlin.concurrent.Volatile

/** Base class for providers for singletons which need to be initialized asynchronously. */
open class AsyncSingletonProvider<T>(private val providerFunction: suspend () -> T) {
    @Volatile
    private var instance: T? = null
    private val mutex = Mutex()

    /**
     * Returns the singleton instance of the provided class, calling the [providerFunction] on the
     * first call to construct and initialize the object. Thread-safe; concurrent callers will block
     * until the singleton is created and returned.
     */
    suspend fun get(): T =
        instance ?: mutex.withLock { instance ?: providerFunction().also { instance = it } }
}

fun <T> single(providerFunction: suspend () -> T) = AsyncSingletonProvider(providerFunction)